package view;

import hci.ChatGui;

import java.util.Observable;
import java.util.Observer;

import observerNotification.ChangingStateObserverNotification;
import observerNotification.ConversationCreatedObserverNotification;
import observerNotification.ErrorObserverNotification;
import observerNotification.FileObserverNotification;
import observerNotification.FileTransferFinishedObserverNotification;
import observerNotification.MessageObserverNotification;
import observerNotification.FileAnswerObserverNotification;
import observerNotification.QuestionObserverNotification;
import observerNotification.RemoteUserObserverNotification;
import observerNotification.StartFileShareObserverNotification;
import observerNotification.TongueChangedObserverNotification;

import networkInterface.ChatNI;

import main.ChatSystem;
import model.Model;
import controller.AbstractController;
import controller.Controller;


/**
 * Manages the view.
 * 
 * <p>
 * <ul>
 * <li>Project: 	ChatSystemG3</li>
 * <li>Called by: 	main.ChatSystem</li>
 * </ul>
 * </p>
 * 
 * @author COMBES Nicolas - LECHANTRE Arnaud
 * @see Observer
 */
public class ViewController implements Observer{
	
	private ChatGui gui;						//GUI of the application
	private AbstractController controller;		//Controller of the Application (see MVC pattern)
	private ChatNI networkInterface;
	private String username;
	
	
	/**
	 * default construct; Creates new GUI and controller
	 */
	public ViewController() {
		super();
		this.controller = new Controller(this);
		new Thread(controller).start();
		this.gui=new ChatGui(controller);
		this.username= "unset";
	}


	/**
	 * Update method aimed at receiving and processing messages from other parts of the app.
	 * <p>
	 * Sender o is an AbstractController or an instance of, we do:
	 * <ul>
	 *  <li>this.disconnect</li>
	 *  <li>this.gui.showConfirmDialog</li>
	 *  <li>this.gui.showError</li>
	 *  <li>performChangeTongue</li>
	 * </ul>
	 * depending on arg type.
	 * </p>
	 * <p>
	 * Sender o is a Model or an instance of, we do:
	 * <ul>
	 *  <li>Several message sending including files</li>
	 *  <li>Hello messages receiving/replies to HelloMessages</li>
	 *  <li>...</li>
	 * </ul>
	 * depending on arg type.
	 * </p>
	 * 
	 * @param o: app message sender, used to determine the type of sender
	 * @param arg: a signal represented by an object
	 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
	 */
	@Override
	public void update(Observable o, Object arg) {
		//if debug mode activated, we show the class of the received message
		if(ChatSystem.debugMode)System.out.println("view.ViewController: "+arg.getClass().getName());
		
		//Else we check the type of sender
		if (o instanceof AbstractController){
			//If the message comes from a Controller
			
			if(arg instanceof ChangingStateObserverNotification && ((ChangingStateObserverNotification)arg).state.equals(ChangingStateObserverNotification.EnumState.DISCONNECTED)){
				//If we received a disconnected state, it means the the application has been disconnected so we have to change the mainframe panel 
				this.disconnect(this.username);
			} else if (arg instanceof QuestionObserverNotification){
				//If we received an error, we ask the GUI to display it
				this.gui.showConfirmDialog((QuestionObserverNotification) arg);
			}  else if (arg instanceof ErrorObserverNotification){
				//If we received an error, we ask the GUI to display it
				this.gui.showError(((ErrorObserverNotification) arg).getConvId(), (ErrorObserverNotification) arg);
			} else if (arg instanceof TongueChangedObserverNotification){
				//If we received an error, we ask the GUI to display it
				this.gui.performChangeTongue(((TongueChangedObserverNotification)arg).getNewTongue());
			} 
		}
		
		else if (o instanceof Model){
			//If the message comes from a model
			if(arg instanceof ChangingStateObserverNotification){
				/*
				 * If the user just connects
				 */
				//If we received a connected state, it means the the application connection successes 
				this.username = ((ChangingStateObserverNotification) arg).getUsername();
				this.connect(this.username);
				
			} else if ( arg instanceof RemoteUserObserverNotification ) {
				/*
				 * If the notification informs of the new state of an user
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: "+ ((RemoteUserObserverNotification)arg).getUsername()+" has switched status");
				if ( ((RemoteUserObserverNotification)arg).isConnected() ){
					if(ChatSystem.debugMode)System.out.println("view.ViewController: "+((RemoteUserObserverNotification)arg).getUsername()+" is now connected");
					if ( ((RemoteUserObserverNotification)arg).isBroadcast() ){
						this.networkInterface.sendHello(this.username,((RemoteUserObserverNotification) arg).getAddr());
					}
				} else {
					if(ChatSystem.debugMode)System.out.println("view.ViewController: "+((RemoteUserObserverNotification)arg).getUsername()+" is now disconnected.");
				}
				this.gui.updateConnectedUserList(((RemoteUserObserverNotification)arg).isConnected() , ((RemoteUserObserverNotification)arg).getUsername(),((RemoteUserObserverNotification)arg).getConvId());
				
			} else if ( arg instanceof MessageObserverNotification ) {
				/*
				 * If the notification informs us that we have received a text message
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: ["+((MessageObserverNotification) arg).getUsername()+"] says: "+((MessageObserverNotification) arg).getMessage());
				if(this.username.equals(((MessageObserverNotification) arg).getUsername())){
					//send the message via the network
					this.networkInterface.sendMessage(this.username, ((MessageObserverNotification) arg).getMessage(), ((MessageObserverNotification) arg).getAddr());
					this.gui.showNotification(((MessageObserverNotification) arg).getConvId(), "Message sent");
				} else {
					this.gui.showMessage(((MessageObserverNotification) arg).getConvId(), ((MessageObserverNotification) arg).getUsername(), ((MessageObserverNotification) arg).getMessage());

				}
				
			}  else if ( arg instanceof StartFileShareObserverNotification ) {
				/*
				 * If the notification informs us that a remote user has answered to a file request
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: ["+((StartFileShareObserverNotification) arg).getUsername()+"view.ViewController: ] Answer to file request received: "+((StartFileShareObserverNotification) arg).getFile().getName() + " "+ ((StartFileShareObserverNotification) arg).isStart() );
				if (((StartFileShareObserverNotification) arg).isStart()) {
					this.gui.showNotification(((StartFileShareObserverNotification) arg).getConvId(), "File transfer begining");
					this.networkInterface.sendFile(((StartFileShareObserverNotification) arg).getConvId(), this.username, ((StartFileShareObserverNotification) arg).getFile(), ((StartFileShareObserverNotification) arg).getAddr());
				} else {
					this.gui.showNotification(((StartFileShareObserverNotification) arg).getConvId(), "File transfer refused");
				}
				
			} else if ( arg instanceof FileAnswerObserverNotification ) {
				/*
				 * If the notification informs the view that we have answered to a file request
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: ["+((FileAnswerObserverNotification) arg).getUsername()+"] response to file transfer: "+((FileAnswerObserverNotification) arg).getFileName());
				//send the message via the network
				this.networkInterface.sendFileAnswer(((FileAnswerObserverNotification) arg).getConvId(), this.username,((FileAnswerObserverNotification) arg).getAddr(), ((FileAnswerObserverNotification) arg).getFileName(), ((FileAnswerObserverNotification) arg).getDownloadDirectory() );
				this.gui.showNotification(((FileAnswerObserverNotification) arg).getConvId(), "file answer sent");
				
			}  else if ( arg instanceof FileObserverNotification ) {
				/*
				 * If the notification informs us that a remote user proposes to share a file 
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: ["+((FileObserverNotification) arg).getUsername()+"] purpose to send: "+((FileObserverNotification) arg).getFile().getName());
				if(this.username.equals(((FileObserverNotification) arg).getUsername())){
					//send the message via the network
					this.networkInterface.sendFileRequest(this.username, ((FileObserverNotification) arg).getFile(), ((FileObserverNotification) arg).getAddr());
					this.gui.showNotification(((FileObserverNotification) arg).getConvId(), "file request sent");
				} else {
					this.gui.showFileRequest(((FileObserverNotification) arg).getConvId(), ((FileObserverNotification) arg).getUsername(), ((FileObserverNotification) arg).getFile().getName(), ((FileObserverNotification) arg).getFileLength());
				}
				
			} else if ( arg instanceof ConversationCreatedObserverNotification ) {
				/*
				 * If the notification informs us that a new conversation has been created
				 */
				if(ChatSystem.debugMode)System.out.println("view.ViewController: Oppening conversation with " + ((ConversationCreatedObserverNotification)arg).getStringOfUsers());
				this.gui.showConversation(((ConversationCreatedObserverNotification)arg).getConvId(), ((ConversationCreatedObserverNotification)arg).getUserList(), this.username);
			} else if ( arg instanceof FileTransferFinishedObserverNotification ) {
				/*
				 * If the notification informs us that a new conversation has been created
				 */
				//TODO wat
				if(ChatSystem.debugMode)System.out.println("view.ViewController: End of transfer notification received in conversation " + ((FileTransferFinishedObserverNotification)arg).getConvId());
				this.gui.showNotification(((FileTransferFinishedObserverNotification)arg).getConvId(), ((FileTransferFinishedObserverNotification)arg).getNotification());
			}
		}
	}
	
	/**
	 * Processes a connection.
	 * @param username
	 */
	public void connect(String username){
		try {
			this.networkInterface= new ChatNI(this.controller);
			this.networkInterface.broadcastHello(username);
			this.gui.showConnectedView(username);
		} catch (Exception e) {
			this.controller.performDisconnect();
		}
	}
	
	/**
	 * Sends a disconnection signal to all
	 */
	public void disconnect(String username){
		if(this.networkInterface!=null)this.networkInterface.sendGoodbye(username);
		this.gui.showDisconnectedView();
	}
	
}
